package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.*;
import com.heima.article.service.ApArticleService;
import com.heima.common.exception.CustException;
import com.heima.feigns.behavior.BehaviorFeign;
import com.heima.feigns.user.UserFeign;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.dtos.HotArticleVo;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.pojos.ApAuthor;
import com.heima.model.behavior.dtos.ApArticleRelationDto;
import com.heima.model.behavior.pojos.ApCollection;
import com.heima.model.common.constants.ArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.ArticleVisitStreamMess;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.user.pojos.ApUserFollow;
import com.heima.utils.threadlocal.AppThreadLocalUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;


@Service
@Transactional
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {


    @Autowired
    AuthorMapper authorMapper;

    @Autowired
    ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    ApArticleContentMapper apArticleContentMapper;

    @Autowired
    ApArticleMapper apArticleMapper;

    @Autowired
    UserFeign userFeign;

    @Autowired
    BehaviorFeign behaviorFeign;

    @Autowired
    ApCollectionMapper apCollectionMapper;

    @Autowired
    KafkaTemplate kafkaTemplate;

    @Value("${file.oss.web-site}")
    private String website;


    @Autowired
    StringRedisTemplate redisTemplate;



    /**
     * 根据参数加载文章列表  v2
     *
     * @param loadtypeLoadMore
     * @param dto
     * @param firstPage
     * @return
     */
    @Override
    public ResponseResult load2(Short loadtypeLoadMore, ArticleHomeDto dto, boolean firstPage) {
        if(firstPage){
            String jsonStr = (String) redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if(StringUtils.isNotBlank(jsonStr)){
                List<HotArticleVo> hotArticleVoList = JSON.parseArray(jsonStr, HotArticleVo.class);
                if(!hotArticleVoList.isEmpty()&& hotArticleVoList.size() > 0){
                    ResponseResult responseResult = ResponseResult.okResult(hotArticleVoList);
                    responseResult.setHost(website);
                    return responseResult;
                }
            }
        }
        return load(loadtypeLoadMore,dto);
    }


    /**
     * 重新计算文章分值
     *
     * @param mess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {

        log.info("updateApArticle is begin: {}",mess);
        //1 查询文档
        ApArticle apArticle = getById(mess.getArticleId());
        if (apArticle == null) {
            log.error("apArticle is null id:{}", mess.getArticleId());
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //2 修改文章的行为数据（阅读1、点赞3、评论5、收藏8）
        if (mess.getView() != 0) {
            int view = (int) (apArticle.getViews() == null ? mess.getView() : mess.getView() + apArticle.getViews());
            apArticle.setViews(view);
        }
        if (mess.getLike() != 0) {
            int like = (int) (apArticle.getLikes() == null ? mess.getLike() : mess.getLike() + apArticle.getLikes());
            apArticle.setLikes(like);
        }
        if (mess.getComment() != 0) {
            int comment = (int) (apArticle.getComment() == null ? mess.getComment() : mess.getComment() + apArticle.getComment());
            apArticle.setComment(comment);
        }
        if (mess.getCollect() != 0) {
            int collection = (int) (apArticle.getCollection() == null ? mess.getCollect() : mess.getCollect() + apArticle.getCollection());
            apArticle.setCollection(collection);
        }
        updateById(apArticle);

        //3 计算文章分值
        Integer score = computeScore(apArticle);
        score = score*3;  //当天热点数据 *3

        //4 更新缓存（频道）
        String articleListStr = redisTemplate.opsForValue()
                .get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        updateArticleCache(apArticle, score, articleListStr,ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());


        //5 更新推荐列表的缓存
        String articleDefaultStr = redisTemplate.opsForValue()
                .get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ ArticleConstants.DEFAULT_TAG);
        updateArticleCache(apArticle, score, articleDefaultStr, ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ ArticleConstants.DEFAULT_TAG);

        log.info("updateApArticle is success");

    }

    /**
     * 更新文章缓存
     * @param apArticle  当前文章
     * @param score 分数
     * @param articleListStr  缓存文章列表
     * @param tag
     */
    private void updateArticleCache(ApArticle apArticle, Integer score, String articleListStr, String tag) {
        log.info("updateApArticle updateArticleCache apArticle:{},score:{}",apArticle,score);
        boolean flag = false;
        if (StringUtils.isNotBlank(articleListStr)) {
            // 转成 List集合
            List<HotArticleVo> hotArticleVos = JSON.parseArray(articleListStr, HotArticleVo.class);
            //1 如果当前缓存中有当前文章，更新分值
            // 遍历集合, 搜索当前文章是否存在，如果存在则修改分数
            for (HotArticleVo hotArticleVo : hotArticleVos) {
                if (hotArticleVo.getId().equals(apArticle.getId())) {
                    hotArticleVo.setScore(score);
                    flag = true;
                    break;
                }
            }

            //2 缓存中没有当前文章
            if (!flag) {

                // 如果文章列表文章数据大于等于30条记录，获取缓存中的最小分值的当前文章作对比
                if (hotArticleVos.size() >= 30) {
                    // 排序
                    hotArticleVos = hotArticleVos.stream()
                            .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                            .collect(Collectors.toList());

                    HotArticleVo hotArticleVo = hotArticleVos.get(hotArticleVos.size() - 1);
                    //缓存频道下的最后一条文章的分值小于当前文章分值 则替换
                    if (hotArticleVo.getScore() < score) {
                        // 先删除后添加
                        hotArticleVos.remove(hotArticleVo);

                        HotArticleVo hotArticle = new HotArticleVo();
                        BeanUtils.copyProperties(apArticle, hotArticle);
                        hotArticle.setScore(score);

                        hotArticleVos.add(hotArticle);
                    }

                } else{ // 热点文章不足30条，添加到热点文章列表中
                    HotArticleVo hot = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle, hot);
                    hot.setScore(score);
                    hotArticleVos.add(hot);
                }

                // 更新到缓存
                // ****排序
                hotArticleVos = hotArticleVos.stream()
                        .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                        .collect(Collectors.toList());

                redisTemplate.opsForValue().set(tag,
                        JSON.toJSONString(hotArticleVos));

                log.info("updateApArticle updateArticleCache success");
            }
        }
    }
    /**
     * 2.1计算文章分值算法
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        int score = 0;
        // 阅读 1
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        // 点赞 3
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论 5
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        return score;
    }




    /**
     * 加载文章详情的初始化配置信息，比如关注、喜欢、不喜欢等
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        //参数校验
        if (dto == null || dto.getArticleId() == null || dto.getAuthorId() == null) {
            return ResponseResult.okResult(new HashMap<>());
        }

        boolean iscollection = false, isfollow = false;

        ApUser user = AppThreadLocalUtils.getUser();
        if (user == null) {
            return ResponseResult.okResult(new HashMap<>());
        }
        //查询作者对应的Apuser主键
        ApAuthor apAuthor = authorMapper.selectById(dto.getAuthorId());

        if (apAuthor == null) {
            return ResponseResult.okResult(new HashMap<>());
        }

        ApUserFollow userFollow = userFeign.findByUserIdAndFollowId(user.getId(), apAuthor.getUserId());
        if (userFollow != null) {
            isfollow = true;
        }

        //查询点赞、不喜欢
        ApArticleRelationDto apArticleRelationDto = new ApArticleRelationDto();
        apArticleRelationDto.setArticleId(dto.getArticleId());
        apArticleRelationDto.setEntryId(AppThreadLocalUtils.getUser().getId());
        apArticleRelationDto.setType((short) 1);
        Map map = behaviorFeign.findApArticleRelation(apArticleRelationDto);

        //查询收藏
        if (map.get("entryId") != null) {
            ApCollection apCollection = apCollectionMapper.selectOne(Wrappers.<ApCollection>lambdaQuery()
                    .eq(ApCollection::getArticleId, dto.getArticleId())
                    .eq(ApCollection::getEntryId, map.get("entryId"))
                    .eq(ApCollection::getType, ApCollection.Type.ARTICLE.getCode())
            );

            if (apCollection != null) {
                iscollection = true;
            }
        }

        //3 返回结果
        Map resultMap = new HashMap();
        resultMap.put("isfollow", isfollow);
        resultMap.put("iscollection", iscollection);
        resultMap.putAll(map);

        return ResponseResult.okResult(resultMap);
    }

    /**
     * 加载文章详情
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult loadArtilceInfo(ArticleInfoDto dto) {
        //参数校验
        if (dto == null || dto.getArticleId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        Long articleId = dto.getArticleId();

        ApArticleConfig apArticleConfig = apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery()
                .eq(ApArticleConfig::getArticleId, articleId)
        );

        if (apArticleConfig == null || apArticleConfig.getIsDown() || apArticleConfig.getIsDelete()) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章已删除或者下架");
        }

        ApArticleContent apArticleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery()
                .eq(ApArticleContent::getArticleId, articleId)
        );

        if (apArticleContent == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");

        }

        Map<String, Object> map = new HashMap<>();
        map.put("config", apArticleConfig);
        map.put("content", apArticleContent);


        return ResponseResult.okResult(map);
    }

    /**
     * 根据参数加载文章列表
     *
     * @param loadtype 1为加载更多  2为加载最新
     * @param dto
     * @return
     */
    @Override
    public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
        //参数校验
        if (dto == null) {
            new ArticleHomeDto();
        }

        Integer size = dto.getSize();
        if (size == null || size <= 0) {
            size = 10;
        }
        size = Math.min(size, 50);
        dto.setSize(size);

        if (StringUtils.isBlank(dto.getTag())) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }

        if (dto.getMaxBehotTime() == null) {
            dto.setMaxBehotTime(new Date());
        }

        if (dto.getMinBehotTime() == null) {
            dto.setMinBehotTime(new Date());
        }


        if (!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE) &&
                !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)) {
            loadtype = ArticleConstants.LOADTYPE_LOAD_MORE;
        }

        List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadtype);

        ResponseResult result = ResponseResult.okResult(apArticles);

        result.setHost(website);


        return result;
    }

    /**
     * 保存或修改文章
     *
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveArtilce(ArticleDto articleDto) {
        //参数检查
        if (articleDto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        //保存文章数据，设置作者id
        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(articleDto, apArticle);
        ApAuthor apAuthor = authorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery()
                .eq(ApAuthor::getName, apArticle.getAuthorName()));

        if (apAuthor == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //设置作者id
        apArticle.setAuthorId(apAuthor.getId().longValue());


        if (articleDto.getId() == null) {//保存
            // 普通文章
            apArticle.setComment(0);
            apArticle.setViews(0);
            apArticle.setCollection(0);
            apArticle.setLikes(0);
            // 保存
            save(apArticle);  // 返回ID

            //保存article_config 数据
            ApArticleConfig apArticleConfig = new ApArticleConfig();
            apArticleConfig.setIsForward(true);
            apArticleConfig.setIsComment(true);
            apArticleConfig.setIsDown(false);
            apArticleConfig.setIsDelete(false);
            //文章id
            apArticleConfig.setArticleId(apArticle.getId());
            apArticleConfigMapper.insert(apArticleConfig);

            //保存文章article_content 数据
            ApArticleContent apArticleContent = new ApArticleContent();
            apArticleContent.setContent(articleDto.getContent());
            // 文章ID
            apArticleContent.setArticleId(apArticle.getId());
            apArticleContentMapper.insert(apArticleContent);

        } else {//修改
            ApArticle article = getById(articleDto.getId());
            if (article == null) {
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
            }

            updateById(apArticle);

            ApArticleContent articleContent = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery()
                    .eq(ApArticleContent::getArticleId, articleDto.getId()));
            if (articleContent == null) {
                log.info("articleContent getById is null: {}", articleDto.getId());
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
            }
            articleContent.setContent(articleDto.getContent());
            apArticleContentMapper.updateById(articleContent);

        }

        //导入数据到es索引库  发送消息
        Map<String, Object> map = new HashMap<>();
        map.put("id", apArticle.getId().toString());
        map.put("publishTime", apArticle.getPublishTime());
        map.put("layout", apArticle.getLayout());
        map.put("images", apArticle.getImages());
        map.put("authorId", apArticle.getAuthorId());
        map.put("title",apArticle.getTitle());
        kafkaTemplate.send(ArticleConstants.ARTICL_ES_SYNC_TOPIC, JSON.toJSONString(map));


        return ResponseResult.okResult(apArticle.getId());
    }
}